﻿// DlgCalibration.cpp : 구현 파일입니다.
//

#include "stdafx.h"
#include "MtCtrl.h"
#include "MtCtrlDlg.h"
#include "DlgCalibration.h"
#include "afxdialogex.h"
#include "MemDC2.h"
#include <math.h>


void maInit(PMA_DATA pMA)
{
	pMA->nCount = 0;
	memset(pMA->fSamples, 0, sizeof(pMA->fSamples));
	pMA->fTotal = 0;
}
float maGetData(PMA_DATA pMA)
{
	if (0 == pMA->nCount)
		return 0;

	return (float)(pMA->fTotal / ((MA_MAX_NUM > pMA->nCount) ? pMA->nCount : MA_MAX_NUM));
}
float maAddData(PMA_DATA pMA, float fData)
{
	int idx = (pMA->nCount % MA_MAX_NUM);
	float old = (MA_MAX_NUM > pMA->nCount) ? 0 : pMA->fSamples[idx];

	pMA->fSamples[idx] = fData;
	pMA->fTotal += (fData - old);
	pMA->nCount++;

	return maGetData(pMA);
}


// CDlgCalibration 대화 상자입니다.

IMPLEMENT_DYNAMIC(CDlgCalibration, CDialogEx)
	CDlgCalibration::CDlgCalibration(CWnd* pParent /*=NULL*/)
	: CDialogEx(CDlgCalibration::IDD, pParent)
{
	m_nCalibrationStep = CALIBRATION_DRAW;
	m_nScreenRadian = 0;
	memset(m_fScreenRadians, 0, sizeof(m_fScreenRadians));
	QueryPerformanceFrequency(&m_freq);
	m_nDrawCount = 0;
}

CDlgCalibration::~CDlgCalibration()
{
}

void CDlgCalibration::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CDlgCalibration, CDialogEx)
	ON_WM_DESTROY()
	ON_WM_PAINT()
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_WM_ERASEBKGND()
	ON_BN_CLICKED(IDC_BUTTON_START, &CDlgCalibration::OnBnClickedButtonStart)
	ON_BN_CLICKED(IDC_BUTTON_EXIT, &CDlgCalibration::OnBnClickedButtonExit)
	ON_WM_RBUTTONDOWN()
	ON_WM_TIMER()
END_MESSAGE_MAP()

// CDlgCalibration 메시지 처리기입니다.

void CDlgCalibration::showWindow(BOOL bIsShow)
{
	CMtCtrlDlg *pParent = (CMtCtrlDlg *)GetParent();

	if (bIsShow)
	{
		CWnd::GetDesktopWindow()->GetWindowRect(m_rect);
		SetWindowPos(&wndTopMost, m_rect.left, m_rect.top, m_rect.right, m_rect.bottom, SWP_SHOWWINDOW);

		m_nCalibrationStep = CALIBRATION_DRAW;
		GetDlgItem(IDC_BUTTON_START)->ShowWindow(SW_SHOW);
		GetDlgItem(IDC_BUTTON_EXIT)->ShowWindow(SW_SHOW);

		chageCurPoint(0);
	}
	else
	{
		BYTE bState = STATE_TOUCH;
		pParent->sendReportAll(CMD_SET_STATE, sizeof(bState), (LPBYTE)&bState);
	}

	ShowWindow(bIsShow ? SW_SHOW : SW_HIDE);
}

void CDlgCalibration::chageCurPoint(int nCurPoint)
{
	m_nScreenRadian = nCurPoint;
	RedrawWindow();

	if (DATA_COEFFS_NUM == m_nScreenRadian) {
		CMtCtrlDlg *pParent = (CMtCtrlDlg *)GetParent();
		for (int c=0; c<CAMERA_MAX_NUM; c++) {
			float fCoeffs[DATA_COEFFS_NUM] = {0,};
			for (int i=0; i<DATA_COEFFS_NUM; i++) {
				pParent->m_CCD[c].fCoeffs[i] = fCoeffs[i] = tanf(m_fScreenRadians[c][i]);
				pParent->logMsg(("%d - %d - %f"), c, i, fCoeffs[i]);
			}
			pParent->sendReport(CMD_SET_COEFFS, (DEV_CAMERA1 + c), sizeof(fCoeffs), (LPBYTE)&fCoeffs);
		}
		updateOrigin();

		m_nCalibrationStep = CALIBRATION_DRAW;
		m_nScreenRadian = 0;
		memset(m_fScreenRadians, 0, sizeof(m_fScreenRadians));
		GetDlgItem(IDC_BUTTON_START)->ShowWindow(SW_SHOW);
		GetDlgItem(IDC_BUTTON_EXIT)->ShowWindow(SW_SHOW);
	} else if (m_nScreenRadian) {
		m_nCalibrationStep = CALIBRATION_WAIT;
		SetTimer(TID_WAIT, DELAY_WAIT, NULL);
	}
}

enum {
	IDX_SX=0, IDX_LX, IDX_CX, IDX_RX, IDX_EX,
	IDX_SY=0, IDX_CY, IDX_EY,
};
static const float LINES_X[] = {
	PERCENT_TO_POINT(DATA_COEFFS_MARGIN_SX),	//IDX_SX
	PERCENT_TO_POINT(DATA_COEFFS_MARGIN_LX),	//IDX_LX
	PERCENT_TO_POINT(DATA_COEFFS_MARGIN_CX),	//IDX_CX
	PERCENT_TO_POINT(DATA_COEFFS_MARGIN_RX),	//IDX_RX
	PERCENT_TO_POINT(DATA_COEFFS_MARGIN_EX)		//IDX_EX
};
static const float LINES_Y[] = {
	PERCENT_TO_POINT(DATA_COEFFS_MARGIN_SY),	//IDX_SY
	PERCENT_TO_POINT(DATA_COEFFS_MARGIN_CY),	//IDX_CY
	PERCENT_TO_POINT(DATA_COEFFS_MARGIN_EY)		//IDX_EY
};
static void point_x_position(float tan1, float tan2, float tan3, float x1, float x3, float y, PPOINT_FLOAT pPt)
{
	pPt->x = x1 + (x3 - x1) * (tan3 * (tan1 - tan2)) / (tan2 * (tan1 - tan3));
	pPt->y = y;
}
static void point_y_position(float tan1, float tan2, float tan3, float y1, float y3, float x, PPOINT_FLOAT pPt)
{
	pPt->x = x;
	pPt->y = y1 + (y3 - y1) * (tan2 - tan1) / (tan3 - tan1);
}
static int radian_lineeq(BYTE bDev, PCAMERACONFIG_DATA pCCD, PRADIAN_DATA pRd, PLINEEQ_DATA pLd, int maxCount)
{
	POINT_FLOAT fPts[2] = {0,};
	float fCoeff[3];

	for (int i=0; i<maxCount; i++) {
		fCoeff[0] = tanf(pRd[i].fCenter);
		fCoeff[1] = tanf(pRd[i].fStart);
		fCoeff[2] = tanf(pRd[i].fEnd);
		for (int j=0; j<3; j++) {
			switch (bDev) {
			case DEV_CAMERA1:
				if (pCCD->fCoeffs[2] > fCoeff[j]) {
					point_x_position(pCCD->fCoeffs[2], fCoeff[j], pCCD->fCoeffs[3], LINES_X[IDX_RX], LINES_X[IDX_EX], LINES_Y[IDX_SY], &fPts[0]);
					point_y_position(pCCD->fCoeffs[3], fCoeff[j], pCCD->fCoeffs[4], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_EX], &fPts[1]);
				} else if (pCCD->fCoeffs[1] > fCoeff[j]) {
					point_x_position(pCCD->fCoeffs[1], fCoeff[j], pCCD->fCoeffs[2], LINES_X[IDX_LX], LINES_X[IDX_RX], LINES_Y[IDX_SY], &fPts[0]);
					point_y_position(pCCD->fCoeffs[2], fCoeff[j], pCCD->fCoeffs[5], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_RX], &fPts[1]);
				} else if (pCCD->fCoeffs[5] > fCoeff[j]) {
					point_y_position(pCCD->fCoeffs[1], fCoeff[j], pCCD->fCoeffs[7], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_LX], &fPts[0]);
					point_y_position(pCCD->fCoeffs[2], fCoeff[j], pCCD->fCoeffs[5], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_RX], &fPts[1]);
				} else if (pCCD->fCoeffs[7] > fCoeff[j]) {
					point_y_position(pCCD->fCoeffs[1], fCoeff[j], pCCD->fCoeffs[7], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_LX], &fPts[0]);
					point_x_position(pCCD->fCoeffs[7], fCoeff[j], pCCD->fCoeffs[5], LINES_X[IDX_LX], LINES_X[IDX_RX], LINES_Y[IDX_EY], &fPts[1]);
				} else {
					point_x_position(pCCD->fCoeffs[8], fCoeff[j], pCCD->fCoeffs[7], LINES_X[IDX_SX], LINES_X[IDX_LX], LINES_Y[IDX_EY], &fPts[0]);
					point_y_position(pCCD->fCoeffs[0], fCoeff[j], pCCD->fCoeffs[8], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_SX], &fPts[1]);
					//point_y_position(pCCD->fCoeffs[1], fCoeff[j], pCCD->fCoeffs[7], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_LX], &fPts[1]);
				}
				break;
			case DEV_CAMERA2:
				if (pCCD->fCoeffs[1] > fCoeff[j]) {
					point_x_position(pCCD->fCoeffs[0], fCoeff[j], pCCD->fCoeffs[1], LINES_X[IDX_SX], LINES_X[IDX_LX], LINES_Y[IDX_SY], &fPts[0]);
					point_y_position(pCCD->fCoeffs[0], fCoeff[j], pCCD->fCoeffs[8], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_SX], &fPts[1]);
				} else if (pCCD->fCoeffs[2] > fCoeff[j]) {
					point_x_position(pCCD->fCoeffs[1], fCoeff[j], pCCD->fCoeffs[2], LINES_X[IDX_LX], LINES_X[IDX_RX], LINES_Y[IDX_SY], &fPts[0]);
					point_y_position(pCCD->fCoeffs[1], fCoeff[j], pCCD->fCoeffs[7], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_LX], &fPts[1]);
				} else if (pCCD->fCoeffs[7] > fCoeff[j]) {
					point_y_position(pCCD->fCoeffs[2], fCoeff[j], pCCD->fCoeffs[5], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_RX], &fPts[0]);
					point_y_position(pCCD->fCoeffs[1], fCoeff[j], pCCD->fCoeffs[7], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_LX], &fPts[1]);
				} else if (pCCD->fCoeffs[5] > fCoeff[j]) {
					point_y_position(pCCD->fCoeffs[2], fCoeff[j], pCCD->fCoeffs[5], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_RX], &fPts[0]);
					point_x_position(pCCD->fCoeffs[7], fCoeff[j], pCCD->fCoeffs[5], LINES_X[IDX_LX], LINES_X[IDX_RX], LINES_Y[IDX_EY], &fPts[1]);
				} else {
					point_x_position(pCCD->fCoeffs[5], fCoeff[j], pCCD->fCoeffs[4], LINES_X[IDX_RX], LINES_X[IDX_EX], LINES_Y[IDX_EY], &fPts[0]);
					point_y_position(pCCD->fCoeffs[3], fCoeff[j], pCCD->fCoeffs[4], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_EX], &fPts[1]);
					//point_y_position(pCCD->fCoeffs[2], fCoeff[j], pCCD->fCoeffs[5], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_RX], &fPts[1]);
				}
				break;

			case DEV_CAMERA3:
				if (pCCD->fCoeffs[2] > fCoeff[j]) {
					point_x_position(pCCD->fCoeffs[2], fCoeff[j], pCCD->fCoeffs[3], LINES_X[IDX_RX], LINES_X[IDX_EX], LINES_Y[IDX_SY], &fPts[0]);
					point_y_position(pCCD->fCoeffs[3], fCoeff[j], pCCD->fCoeffs[4], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_EX], &fPts[1]);
				} else if (pCCD->fCoeffs[11] > fCoeff[j]) {
					point_y_position(pCCD->fCoeffs[2], fCoeff[j], pCCD->fCoeffs[11], LINES_Y[IDX_SY], LINES_Y[IDX_CY], LINES_X[IDX_RX], &fPts[0]);
					point_y_position(pCCD->fCoeffs[3], fCoeff[j], pCCD->fCoeffs[4], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_EX], &fPts[1]);
				} else if (pCCD->fCoeffs[10] > fCoeff[j]) {
					point_x_position(pCCD->fCoeffs[10], fCoeff[j], pCCD->fCoeffs[11], LINES_X[IDX_CX], LINES_X[IDX_RX], LINES_Y[IDX_CY], &fPts[0]);
					point_x_position(pCCD->fCoeffs[6], fCoeff[j], pCCD->fCoeffs[4], LINES_X[IDX_CX], LINES_X[IDX_EX], LINES_Y[IDX_EY], &fPts[1]);
				} else {
					point_x_position(pCCD->fCoeffs[10], fCoeff[j], pCCD->fCoeffs[11], LINES_X[IDX_CX], LINES_X[IDX_RX], LINES_Y[IDX_CY], &fPts[0]);
					point_x_position(pCCD->fCoeffs[6], fCoeff[j], pCCD->fCoeffs[5], LINES_X[IDX_CX], LINES_X[IDX_RX], LINES_Y[IDX_EY], &fPts[1]);
				}
				break;
			case DEV_CAMERA4:
				if (pCCD->fCoeffs[1] > fCoeff[j]) {
					point_x_position(pCCD->fCoeffs[0], fCoeff[j], pCCD->fCoeffs[1], LINES_X[IDX_SX], LINES_X[IDX_LX], LINES_Y[IDX_SY], &fPts[0]);
					point_y_position(pCCD->fCoeffs[0], fCoeff[j], pCCD->fCoeffs[8], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_SX], &fPts[1]);
				} else if (pCCD->fCoeffs[9] > fCoeff[j]) {
					point_y_position(pCCD->fCoeffs[1], fCoeff[j], pCCD->fCoeffs[9], LINES_Y[IDX_SY], LINES_Y[IDX_CY], LINES_X[IDX_LX], &fPts[0]);
					point_y_position(pCCD->fCoeffs[0], fCoeff[j], pCCD->fCoeffs[8], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_SX], &fPts[1]);
				} else if (pCCD->fCoeffs[10] > fCoeff[j]) {
					point_x_position(pCCD->fCoeffs[9], fCoeff[j], pCCD->fCoeffs[10], LINES_X[IDX_LX], LINES_X[IDX_CX], LINES_Y[IDX_CY], &fPts[0]);
					point_x_position(pCCD->fCoeffs[8], fCoeff[j], pCCD->fCoeffs[6], LINES_X[IDX_SX], LINES_X[IDX_CX], LINES_Y[IDX_EY], &fPts[1]);
				} else {
					point_x_position(pCCD->fCoeffs[9], fCoeff[j], pCCD->fCoeffs[10], LINES_X[IDX_LX], LINES_X[IDX_CX], LINES_Y[IDX_CY], &fPts[0]);
					point_x_position(pCCD->fCoeffs[7], fCoeff[j], pCCD->fCoeffs[6], LINES_X[IDX_LX], LINES_X[IDX_CX], LINES_Y[IDX_EY], &fPts[1]);
				}
				break;
			}

			// y = ax + b
			pLd[i].fA[j] = (fPts[1].y - fPts[0].y) / (fPts[1].x - fPts[0].x);
			pLd[i].fB[j] = fPts[0].y - (pLd[i].fA[j] * fPts[0].x);
		}
	}

	return maxCount;
}
static void lineeq_intersection(PLINEEQ_DATA pL0, PLINEEQ_DATA pL1, PPOINT_DATA pd)
{
#if 0
	if (pL2) {
		float fA = pL0->fA[0] + pL1->fA[0] + pL2->fA[0];	// a1 + a2 + a3
		float fB = pL0->fB[0] + pL1->fB[0] + pL2->fB[0];	// b1 + b2 + b3
		float fC = pL0->fA[0]*pL0->fB[0] + pL1->fA[0]*pL1->fB[0] + pL2->fA[0]*pL2->fB[0];	// a1*b1 + a2*b2 + a3*b3
		float fM = pL0->fA[0]*pL0->fA[0] + pL1->fA[0]*pL1->fA[0] + pL2->fA[0]*pL2->fA[0];	// a1*a1 + a2*a2 + a3*a3
		pd->fPt.x = (fA*fB - 3*fC) / (3*fM - fA*fA);
		pd->fPt.y = (fM*fB - fA*fC) / (3*fM - fA*fA);
	} else {
		pd->fPt.x = (pL1->fB[0] - pL1->fB[0]) / (pL1->fA[0] - pL1->fA[0]);
		pd->fPt.y = (pd->fPt.x * pL1->fA[0]) + pL1->fB[0];
	}
#else
	pd->fPt.x = (pL1->fB[0] - pL0->fB[0]) / (pL0->fA[0] - pL1->fA[0]);
	pd->fPt.y = (pd->fPt.x * pL0->fA[0]) + pL0->fB[0];
#endif

	if (1 >= pd->fPt.y) {
		pd->bStatus = 1;

#if 0
		// x축 보정
		if (0.01 > pd->fPt.x) {
			pd->fPt.x -= (0.01 - pd->fPt.x);
			if (0 > pd->fPt.x)
				pd->fPt.x = 0;
		} else if (0.99 < pd->fPt.x) {
			pd->fPt.x += (pd->fPt.x - 0.99);
			if (1 < pd->fPt.x)
				pd->fPt.x = 1;
		}

		// y축 보정
		if (0 > pd->fPt.y) {
			pd->fPt.y = 0;
		} else if (0.99 < pd->fPt.y) {
			pd->fPt.y += (pd->fPt.y - 0.99);
			if (1 < pd->fPt.y)
				pd->fPt.y = 1;
		}
#endif // 0

		// distance(d) from a point(x1, y1) to a line(ax + by + c = 0)
		// d = fabs(a*x1 + b*y1 + c) / sqrt(a*a + b*b)
		// y = Ax + B -> Ax - y + B = 0
		// a = A, b = -1, c = B
		// d = fabs(A*x1 - y1 + B) / sqrt(A*A + 1)
		float fDist1 = fabs((pL0->fA[1]*pd->fPt.x) - pd->fPt.y + pL0->fB[1]) / sqrt((pL0->fA[1]*pL0->fA[1]) + 1);
		float fDist2 = fabs((pL1->fA[1]*pd->fPt.x) - pd->fPt.y + pL1->fB[1]) / sqrt((pL1->fA[1]*pL1->fA[1]) + 1);
		pd->fArea = fDist1 + fDist2;
		//pd->fRatio = 0;
	} else {
		pd->bStatus = 0;
	}
}

static float point_make_subdist(PPOINT_FLOAT pPt, PLINEEQ_DATA pLE)
{
	float fDist, fMinDist = 1000;

	for (int k=0; k<3; k++) {
		fDist = fabs((pLE->fA[k]*pPt->x) - pPt->y + pLE->fB[k]) / sqrt((pLE->fA[k]*pLE->fA[k]) + 1);
		if (fMinDist > fDist) {
			fMinDist = fDist;
		}
	}

	return fMinDist;
}

#undef	RADIAN_DATA_MAX_NUM
#undef	LINEEQ_DATA_MAX_NUM
#define	RADIAN_DATA_MAX_NUM		10
#define	LINEEQ_DATA_MAX_NUM		10
static RADIAN_DATA _RadianData[CAMERA_MAX_NUM][RADIAN_DATA_MAX_NUM] = {0,};
static LINEEQ_DATA _LineEqData[CAMERA_MAX_NUM][LINEEQ_DATA_MAX_NUM] = {0,};
static BYTE _bRadianData[CAMERA_MAX_NUM] = {0,};
static BYTE _bRadianRecv = 0;
void CDlgCalibration::setRadianData(BYTE bCmd, BYTE bDev, BYTE bDLen, BYTE *pData)
{
	BYTE bIdx = bDev - DEV_CAMERA1;	// CAMERA_IDn

	if (CAMERA_MAX_NUM > bIdx) {
		CMtCtrlDlg *pParent = (CMtCtrlDlg *)GetParent();
		int nCount = (bDLen / sizeof(RADIAN_DATA));

		if (RADIAN_DATA_MAX_NUM >= nCount) {
			if (nCount) {
				memcpy(&_RadianData[bIdx][0], pData, bDLen);
				radian_lineeq(bDev, &pParent->m_CCD[bIdx], _RadianData[bIdx], _LineEqData[bIdx], nCount);
			}
			_bRadianData[bIdx] = nCount;

			_bRadianRecv |= (1<<bIdx);	// 0x3 <= ((1<<CAMERA_ID0) && (1<<CAMERA_ID1))
			if (0x3 != (0x3 & _bRadianRecv)) {
				return;
			}

			if (CALIBRATION_DRAW == m_nCalibrationStep) {
				drawRadianData();
			} else if (CALIBRATION_READ == m_nCalibrationStep) {
				BOOL bIsProc = TRUE;
				if (2 < CAMERA_MAX_NUM) {
					switch (_bRadianRecv) {
					case 0xB:	// 화면 좌측 : (1<<3) | (0<<2) | (1<<1) | (1<<0)
						if ((0 == m_nScreenRadian) || (1 == m_nScreenRadian) || (7 == m_nScreenRadian) || (8 == m_nScreenRadian) || (9 == m_nScreenRadian)) {
						} else {
							bIsProc = FALSE;
						}
						break;
					case 0x7:	// 화면 우측 : (0<<3) | (1<<2) | (1<<1) | (1<<0)
						if ((2 == m_nScreenRadian) || (3 == m_nScreenRadian) || (4 == m_nScreenRadian) || (5 == m_nScreenRadian) || (11 == m_nScreenRadian)) {
						} else {
							bIsProc = FALSE;
						}
						break;
					case 0xF:	// 화면 중앙 : (1<<3) | (1<<2) | (1<<1) | (1<<0)
						if ((6 == m_nScreenRadian) || (10 == m_nScreenRadian)) {
						} else {
							bIsProc = FALSE;
						}
						break;
					default:	// 서브카메라 이미지를 받지 못하면 무시한다.
						bIsProc = FALSE;
						break;
					}
				}

				if (bIsProc) {
					procRadianData();
				}
			}

			memset(_RadianData, 0, sizeof(_RadianData));
			memset(_LineEqData, 0, sizeof(_LineEqData));
			memset(_bRadianData, 0, sizeof(_bRadianData));
			_bRadianRecv = 0;
		}
	}
}

void CDlgCalibration::drawRadianData()
{
	const BYTE *LINE_NUM = _bRadianData;
	POINT_DATA newPoints[POINT_DATA_MAX_NUM] = {0,};
	int nNewPoint = 0;

	for (int l=0; l<LINE_NUM[CAMERA_ID0]; l++) {
		for (int r=0; r<LINE_NUM[CAMERA_ID1]; r++) {
			PPOINT_DATA pPD = &newPoints[nNewPoint++];
			lineeq_intersection(&_LineEqData[CAMERA_ID0][l], &_LineEqData[CAMERA_ID1][r], pPD);
		}
	}

	if (nNewPoint) {
		if (DRAW_PERIOD != ++m_nDrawCount) {
			return;
		}
		m_nDrawCount = 0;

		CClientDC dc2(this);
		CMemDC2 dc(&dc2, &m_rect);
		CString strText;

		dc.Rectangle(m_rect);

		CBrush brushSub(RGB(240, 240, 240));
		CBrush* pOldBrush = dc.SelectObject(&brushSub);
		for (int i=0; i<LINE_NUM[CAMERA_ID2]; i++) {
			POINT Pts[3];
			Pts[0] = m_OrgPt[CAMERA_ID2];
			for (int j=1; j<3; j++) {
				POINT_FLOAT fPt;
				if (2 < _RadianData[CAMERA_ID2][i].fStart) {
					// y=1
					fPt.y = 1;
					fPt.x = (fPt.y - _LineEqData[CAMERA_ID2][i].fB[j]) / _LineEqData[CAMERA_ID2][i].fA[j];
				} else {
					// x=1
					fPt.x = 1;
					fPt.y = (fPt.x * _LineEqData[CAMERA_ID2][i].fA[j]) + _LineEqData[CAMERA_ID2][i].fB[j];
				}
				Pts[j].x = (int)(fPt.x * m_rect.Width() + 0.5);
				Pts[j].y = (int)(fPt.y * m_rect.Height() + 0.5);
			}
			if ((0 <= Pts[2].x) && (0 <= Pts[2].y)) {
				dc.Polygon(Pts, 3);
			}
		}
		for (int i=0; i<LINE_NUM[CAMERA_ID3]; i++) {
			POINT Pts[3];
			Pts[0] = m_OrgPt[CAMERA_ID3];
			for (int j=1; j<3; j++) {
				POINT_FLOAT fPt;
				if (-2 > _RadianData[CAMERA_ID3][i].fStart) {
					// y=1
					fPt.y = 1;
					fPt.x = (fPt.y - _LineEqData[CAMERA_ID3][i].fB[j]) / _LineEqData[CAMERA_ID3][i].fA[j];
				} else {
					// x=0
					fPt.x = 0;
					fPt.y = (fPt.x * _LineEqData[CAMERA_ID3][i].fA[j]) + _LineEqData[CAMERA_ID3][i].fB[j];
				}
				Pts[j].x = (int)(fPt.x * m_rect.Width() + 0.5);
				Pts[j].y = (int)(fPt.y * m_rect.Height() + 0.5);
			}
			if ((0 <= Pts[2].x) && (0 <= Pts[2].y)) {
				dc.Polygon(Pts, 3);
			}
		}
		dc.SelectObject(pOldBrush);

		for (int i=0; i<nNewPoint; i++) {
			PPOINT_DATA pPD = &newPoints[i];
			if (pPD->bStatus) {
				POINT pt = {(int)(pPD->fPt.x * m_rect.Width() + 0.5), (int)(pPD->fPt.y * m_rect.Height() + 0.5)};

				dc.MoveTo(m_OrgPt[CAMERA_ID0].x, m_OrgPt[CAMERA_ID0].y);
				dc.LineTo(pt.x, pt.y);
				dc.LineTo(m_OrgPt[CAMERA_ID1].x, m_OrgPt[CAMERA_ID1].y);
				//strText.Format(_T("%d"), i);
				//dc.TextOut(pt.x, pt.y, strText);
			}
		}
	} else {
		Invalidate();
	}
}
void CDlgCalibration::procRadianData()
{
	static MA_DATA _maData[CAMERA_MAX_NUM] = {0,};
	static CArray<float,float> _arySamples[CAMERA_MAX_NUM];

	CMtCtrlDlg *pParent = (CMtCtrlDlg *)GetParent();
	int nNewPoint = 0;
	double timeElapsed = 0;

	{
		static LARGE_INTEGER _oldTime = {0,};
		LARGE_INTEGER curTime;

		QueryPerformanceCounter(&curTime);
		timeElapsed = (curTime.QuadPart- _oldTime.QuadPart) / (double)(m_freq.QuadPart * 0.001);	// 0.001 : (msec)
#if 0
		CString str;
		str.Format(_T("0x%X - %lf - %lf %lf %lf %lf \r\n"), _bRadianRecv, timeElapsed,
			_RadianData[0][0].fCenter, _RadianData[1][0].fCenter, _RadianData[2][0].fCenter, _RadianData[3][0].fCenter);
		OutputDebugString(str);
#endif
		_oldTime = curTime;
	}

	if (15 < timeElapsed) {
		for (int c=0; c<CAMERA_MAX_NUM; c++) {
			maInit(&_maData[c]);
			_arySamples[c].RemoveAll();
			_arySamples[c].FreeExtra();
		}

		pParent->logMsg(("ERR (Timeout(%lf [mSec]))"), timeElapsed);
		return;
	}

	nNewPoint = min(_bRadianData[CAMERA_ID0]*_bRadianData[CAMERA_ID1], POINT_DATA_MAX_NUM);
	if (1 == nNewPoint) {
		BOOL bIsErr = FALSE;
		for (int c=0; c<CAMERA_MAX_NUM; c++) {
			float fNewRadian = maAddData(&_maData[c], _RadianData[c][0].fCenter);

			_arySamples[c].Add(fNewRadian);	// 새로운 데이터 추가
			if (RADIAN_NUM < _arySamples[c].GetCount()) {
				_arySamples[c].RemoveAt(0);	// 오래된 데이터 삭제
			}

			/*bIsErr = FALSE;
			if (RADIAN_NUM == _arySamples[c].GetCount()) {
				for (int i=1, distErr=0; i<RADIAN_NUM; i++) {
					float fOffset = fabs(_arySamples[c].GetAt(0) - _arySamples[c].GetAt(i));
					if (0.001 < fOffset) {
						if (5 <= ++distErr) {
							bIsErr = TRUE;
							pParent->logMsg(("ERR (%d : offset(%f))"), c, fOffset);
						}
					} else {
						distErr = 0;
					}
					if (bIsErr) {
						break;
					}
				}
			}
			if (bIsErr) {
				break;
			}*/
		}

		if (bIsErr) {
			for (int c=0; c<CAMERA_MAX_NUM; c++) {
				maInit(&_maData[c]);
				_arySamples[c].RemoveAll();
				_arySamples[c].FreeExtra();
			}
		} else if (RADIAN_NUM == _arySamples[0].GetCount()) {
			for (int c=0; c<CAMERA_MAX_NUM; c++) {
				float fTotal = 0;
				for (int i=RADIAN_MARGIN; i<(RADIAN_NUM-RADIAN_MARGIN); i++) {
					fTotal += _arySamples[c].GetAt(i);
				}
				m_fScreenRadians[c][m_nScreenRadian] = fTotal / (RADIAN_NUM - 2*RADIAN_MARGIN);
			}

			if (2 < CAMERA_MAX_NUM) {
				pParent->logMsg(("%d - %f, %f, %f, %f"), m_nScreenRadian,
					m_fScreenRadians[0][m_nScreenRadian], m_fScreenRadians[1][m_nScreenRadian],
					m_fScreenRadians[2][m_nScreenRadian], m_fScreenRadians[3][m_nScreenRadian]);
			} else {
				pParent->logMsg(("%d - %f, %f"), m_nScreenRadian,
					m_fScreenRadians[0][m_nScreenRadian], m_fScreenRadians[1][m_nScreenRadian]);
			}
			{
				BYTE b10msec = 15;
				pParent->sendReport(CMD_SET_BUZZER, DEV_TOUCH, sizeof(b10msec), (LPBYTE)&b10msec);
			}

			if (2 == CAMERA_MAX_NUM) {
				if (5 == m_nScreenRadian) {
					m_nScreenRadian++;	// 6
					m_fScreenRadians[0][m_nScreenRadian] = m_fScreenRadians[1][m_nScreenRadian] = 0;
				} else if (8 == m_nScreenRadian) {
					m_nScreenRadian++;	// 9
					m_fScreenRadians[0][m_nScreenRadian] = m_fScreenRadians[1][m_nScreenRadian] = 0;
					m_nScreenRadian++;	// 10
					m_fScreenRadians[0][m_nScreenRadian] = m_fScreenRadians[1][m_nScreenRadian] = 0;
					m_nScreenRadian++;	// 11
					m_fScreenRadians[0][m_nScreenRadian] = m_fScreenRadians[1][m_nScreenRadian] = 0;
				}
			}
			chageCurPoint(m_nScreenRadian + 1);
			for (int c=0; c<CAMERA_MAX_NUM; c++) {
				maInit(&_maData[c]);
				_arySamples[c].RemoveAll();
				_arySamples[c].FreeExtra();
			}
		}
	} else {
		if (nNewPoint) {
			if (4 == CAMERA_MAX_NUM) {
				pParent->logMsg(("ERR (nNewPoint(%d = %d, %d, %d, %d))"), nNewPoint,
					_bRadianData[CAMERA_ID0], _bRadianData[CAMERA_ID1], _bRadianData[CAMERA_ID2], _bRadianData[CAMERA_ID3]);
			} else {
				pParent->logMsg(("ERR (nNewPoint(%d = %d, %d))"), nNewPoint,
					_bRadianData[CAMERA_ID0], _bRadianData[CAMERA_ID1]);
			}

			for (int c=0; c<CAMERA_MAX_NUM; c++) {
				maInit(&_maData[c]);
				_arySamples[c].RemoveAll();
				_arySamples[c].FreeExtra();
			}
		}
	}
}

static int point_intersection(PPOINT_FLOAT p1, PPOINT_FLOAT p2, PPOINT_FLOAT p3, PPOINT_FLOAT p4, PPOINT_FLOAT pf)
{
	float d = (p1->x - p2->x) * (p3->y - p4->y) - (p1->y - p2->y) * (p3->x - p4->x);

	if (d) {
		float pre = (p1->x*p2->y - p1->y*p2->x);
		float post = (p3->x*p4->y - p3->y*p4->x);

		pf->x = (pre * (p3->x - p4->x) - (p1->x - p2->x) * post) / d;
		pf->y = (pre * (p3->y - p4->y) - (p1->y - p2->y) * post) / d;

		if ((0<=pf->x) && (1>=pf->x) && (0<=pf->y) && (1>=pf->y))
			return 1;
	}

	return 0;
}
void CDlgCalibration::updateOrigin()
{
	CMtCtrlDlg *pParent = (CMtCtrlDlg *)GetParent();
	float *pCoeffs;
	POINT_FLOAT fPt1, fPt2, fPt3, fPt4, fPtOrg;

	pCoeffs = pParent->m_CCD[CAMERA_ID0].fCoeffs;
	point_y_position(pCoeffs[1], pCoeffs[4], pCoeffs[7], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_LX], &fPt1);
	point_y_position(pCoeffs[2], pCoeffs[4], pCoeffs[5], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_RX], &fPt2);
	point_y_position(pCoeffs[1], pCoeffs[5], pCoeffs[7], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_LX], &fPt3);
	point_x_position(pCoeffs[7], pCoeffs[5], pCoeffs[5], LINES_X[IDX_LX], LINES_X[IDX_RX], LINES_Y[IDX_EY], &fPt4);
	point_intersection(&fPt1, &fPt2, &fPt3, &fPt4, &fPtOrg);
	m_OrgPt[CAMERA_ID0].x = (int)(fPtOrg.x * m_rect.Width() + 0.5);
	m_OrgPt[CAMERA_ID0].y = (int)(fPtOrg.y * m_rect.Height() + 0.5);
	pParent->logMsg(("Org-0 = %d, %d"), m_OrgPt[CAMERA_ID0].x, m_OrgPt[CAMERA_ID0].y);

	pCoeffs = pParent->m_CCD[CAMERA_ID1].fCoeffs;
	point_y_position(pCoeffs[2], pCoeffs[8], pCoeffs[5], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_RX], &fPt1);
	point_y_position(pCoeffs[1], pCoeffs[8], pCoeffs[7], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_LX], &fPt2);
	point_x_position(pCoeffs[7], pCoeffs[7], pCoeffs[5], LINES_X[IDX_LX], LINES_X[IDX_RX], LINES_Y[IDX_EY], &fPt3);
	point_y_position(pCoeffs[2], pCoeffs[7], pCoeffs[5], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_RX], &fPt4);
	point_intersection(&fPt1, &fPt2, &fPt3, &fPt4, &fPtOrg);
	m_OrgPt[CAMERA_ID1].x = (int)(fPtOrg.x * m_rect.Width() + 0.5);
	m_OrgPt[CAMERA_ID1].y = (int)(fPtOrg.y * m_rect.Height() + 0.5);
	pParent->logMsg(("Org-1 = %d, %d"), m_OrgPt[CAMERA_ID1].x, m_OrgPt[CAMERA_ID1].y);

	if (2 == CAMERA_MAX_NUM) {
		return;
	}

	pCoeffs = pParent->m_CCD[CAMERA_ID2].fCoeffs;
	point_y_position(pCoeffs[2], pCoeffs[4], pCoeffs[5], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_RX], &fPt1);
	point_y_position(pCoeffs[3], pCoeffs[4], pCoeffs[4], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_EX], &fPt2);
	point_x_position(pCoeffs[10], pCoeffs[5], pCoeffs[11], LINES_X[IDX_CX], LINES_X[IDX_RX], LINES_Y[IDX_CY], &fPt3);
	point_x_position(pCoeffs[5], pCoeffs[5], pCoeffs[4], LINES_X[IDX_RX], LINES_X[IDX_EX], LINES_Y[IDX_EY], &fPt4);
	point_intersection(&fPt1, &fPt2, &fPt3, &fPt4, &fPtOrg);
	m_OrgPt[CAMERA_ID2].x = (int)(fPtOrg.x * m_rect.Width() + 0.5);
	m_OrgPt[CAMERA_ID2].y = (int)(fPtOrg.y * m_rect.Height() + 0.5);
	pParent->logMsg(("Org-2 = %d, %d"), m_OrgPt[CAMERA_ID2].x, m_OrgPt[CAMERA_ID2].y);

	pCoeffs = pParent->m_CCD[CAMERA_ID3].fCoeffs;
	point_y_position(pCoeffs[1], pCoeffs[8], pCoeffs[7], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_LX], &fPt1);
	point_y_position(pCoeffs[0], pCoeffs[8], pCoeffs[8], LINES_Y[IDX_SY], LINES_Y[IDX_EY], LINES_X[IDX_SX], &fPt2);
	point_x_position(pCoeffs[9], pCoeffs[7], pCoeffs[10], LINES_X[IDX_LX], LINES_X[IDX_CX], LINES_Y[IDX_CY], &fPt3);
	point_x_position(pCoeffs[8], pCoeffs[7], pCoeffs[7], LINES_X[IDX_SX], LINES_X[IDX_LX], LINES_Y[IDX_EY], &fPt4);
	point_intersection(&fPt1, &fPt2, &fPt3, &fPt4, &fPtOrg);
	m_OrgPt[CAMERA_ID3].x = (int)(fPtOrg.x * m_rect.Width() + 0.5);
	m_OrgPt[CAMERA_ID3].y = (int)(fPtOrg.y * m_rect.Height() + 0.5);
	pParent->logMsg(("Org-3 = %d, %d"), m_OrgPt[CAMERA_ID3].x, m_OrgPt[CAMERA_ID3].y);
}


BOOL CDlgCalibration::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// TODO:  여기에 추가 초기화 작업을 추가합니다.
	CWnd::GetDesktopWindow()->GetWindowRect(m_rect);
	SetWindowPos(&wndTopMost, m_rect.left, m_rect.top, m_rect.right, m_rect.bottom, SWP_SHOWWINDOW);

	{
		const int WIDTH=120, HEIGHT=40;
		int nWCenter = m_rect.Width() / 2;
		int nHCenter = m_rect.Height() / 2;
		CRect rect;

		rect.SetRect(nWCenter-10-WIDTH, nHCenter-HEIGHT/2, nWCenter-10, nHCenter+HEIGHT/2);
		GetDlgItem(IDC_BUTTON_START)->MoveWindow(rect);

		rect.SetRect(nWCenter+10, nHCenter-HEIGHT/2, nWCenter+10+WIDTH, nHCenter+HEIGHT/2);
		GetDlgItem(IDC_BUTTON_EXIT)->MoveWindow(rect);
	}

	m_ptScreens[0].x = (int)(m_rect.Width() *  PERCENT_TO_POINT(DATA_COEFFS_MARGIN_SX) + 0.5);
	m_ptScreens[0].y = (int)(m_rect.Height() * PERCENT_TO_POINT(DATA_COEFFS_MARGIN_SY) + 0.5);

	m_ptScreens[1].x = (int)(m_rect.Width() *  PERCENT_TO_POINT(DATA_COEFFS_MARGIN_LX) + 0.5);
	m_ptScreens[1].y = (int)(m_rect.Height() * PERCENT_TO_POINT(DATA_COEFFS_MARGIN_SY) + 0.5);

	m_ptScreens[2].x = (int)(m_rect.Width() *  PERCENT_TO_POINT(DATA_COEFFS_MARGIN_RX) + 0.5);
	m_ptScreens[2].y = (int)(m_rect.Height() * PERCENT_TO_POINT(DATA_COEFFS_MARGIN_SY) + 0.5);

	m_ptScreens[3].x = (int)(m_rect.Width() *  PERCENT_TO_POINT(DATA_COEFFS_MARGIN_EX) + 0.5);
	m_ptScreens[3].y = (int)(m_rect.Height() * PERCENT_TO_POINT(DATA_COEFFS_MARGIN_SY) + 0.5);

	m_ptScreens[4].x = (int)(m_rect.Width() *  PERCENT_TO_POINT(DATA_COEFFS_MARGIN_EX) + 0.5);
	m_ptScreens[4].y = (int)(m_rect.Height() * PERCENT_TO_POINT(DATA_COEFFS_MARGIN_EY) + 0.5);

	m_ptScreens[5].x = (int)(m_rect.Width() *  PERCENT_TO_POINT(DATA_COEFFS_MARGIN_RX) + 0.5);
	m_ptScreens[5].y = (int)(m_rect.Height() * PERCENT_TO_POINT(DATA_COEFFS_MARGIN_EY) + 0.5);

	m_ptScreens[6].x = (int)(m_rect.Width() *  PERCENT_TO_POINT(DATA_COEFFS_MARGIN_CX) + 0.5);
	m_ptScreens[6].y = (int)(m_rect.Height() * PERCENT_TO_POINT(DATA_COEFFS_MARGIN_EY) + 0.5);

	m_ptScreens[7].x = (int)(m_rect.Width() *  PERCENT_TO_POINT(DATA_COEFFS_MARGIN_LX) + 0.5);
	m_ptScreens[7].y = (int)(m_rect.Height() * PERCENT_TO_POINT(DATA_COEFFS_MARGIN_EY) + 0.5);

	m_ptScreens[8].x = (int)(m_rect.Width() *  PERCENT_TO_POINT(DATA_COEFFS_MARGIN_SX) + 0.5);
	m_ptScreens[8].y = (int)(m_rect.Height() * PERCENT_TO_POINT(DATA_COEFFS_MARGIN_EY) + 0.5);

	m_ptScreens[9].x = (int)(m_rect.Width() *  PERCENT_TO_POINT(DATA_COEFFS_MARGIN_LX) + 0.5);
	m_ptScreens[9].y = (int)(m_rect.Height() * PERCENT_TO_POINT(DATA_COEFFS_MARGIN_CY) + 0.5);

	m_ptScreens[10].x = (int)(m_rect.Width() *  PERCENT_TO_POINT(DATA_COEFFS_MARGIN_CX) + 0.5);
	m_ptScreens[10].y = (int)(m_rect.Height() * PERCENT_TO_POINT(DATA_COEFFS_MARGIN_CY) + 0.5);

	m_ptScreens[11].x = (int)(m_rect.Width() *  PERCENT_TO_POINT(DATA_COEFFS_MARGIN_RX) + 0.5);
	m_ptScreens[11].y = (int)(m_rect.Height() * PERCENT_TO_POINT(DATA_COEFFS_MARGIN_CY) + 0.5);

	updateOrigin();

	return TRUE;  // return TRUE unless you set the focus to a control
	// 예외: OCX 속성 페이지는 FALSE를 반환해야 합니다.
}

void CDlgCalibration::OnDestroy()
{
	CDialogEx::OnDestroy();

	// TODO: 여기에 메시지 처리기 코드를 추가합니다.
}

BOOL CDlgCalibration::PreTranslateMessage(MSG* pMsg)
{
	// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
	if (WM_KEYDOWN == pMsg->message)
	{
		if (VK_RETURN == pMsg->wParam)
		{
			showWindow(FALSE);
			return TRUE;
		}
		else if (VK_ESCAPE == pMsg->wParam)
		{
			showWindow(FALSE);
			return TRUE;
		}
	}

	return CDialogEx::PreTranslateMessage(pMsg);
}

BOOL CDlgCalibration::OnEraseBkgnd(CDC* pDC)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	//return TRUE;
	return CDialogEx::OnEraseBkgnd(pDC);
}
void CDlgCalibration::OnPaint()
{
	CPaintDC dc(this); // device context for painting
	// TODO: 여기에 메시지 처리기 코드를 추가합니다.
	// 그리기 메시지에 대해서는 CDialogEx::OnPaint()을(를) 호출하지 마십시오.
	CMemDC2 MemDC(&dc, &m_rect);
	MemDC.Rectangle(m_rect);

	if (CALIBRATION_DRAW != m_nCalibrationStep) {
		CPen defPen(PS_SOLID, 1, RGB(0x00,0x00,0x00)), curPen(PS_SOLID, 2, RGB(0xFF,0x00,0x00)), *pOldPen;
		CMtCtrlDlg *pParent = (CMtCtrlDlg *)GetParent();

		pOldPen = MemDC.SelectObject(&defPen);
		for (int i=0; i<DATA_COEFFS_NUM; i++) {
			if (2 == CAMERA_MAX_NUM) {
				if ((6==i) || (9==i) || (10==i) || (11==i)) {
					continue;
				}
			}

			if (m_nScreenRadian == i)
				MemDC.SelectObject(&curPen);
			else
				MemDC.SelectObject(&defPen);

			MemDC.MoveTo(m_ptScreens[i].x - 20, m_ptScreens[i].y);
			MemDC.LineTo(m_ptScreens[i].x + 20, m_ptScreens[i].y);

			MemDC.MoveTo(m_ptScreens[i].x, m_ptScreens[i].y - 20);
			MemDC.LineTo(m_ptScreens[i].x, m_ptScreens[i].y + 20);
		}

		MemDC.SelectObject(pOldPen);
	}
}

void CDlgCalibration::OnBnClickedButtonStart()
{
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	m_nCalibrationStep = CALIBRATION_READ;
	GetDlgItem(IDC_BUTTON_START)->ShowWindow(SW_HIDE);
	GetDlgItem(IDC_BUTTON_EXIT)->ShowWindow(SW_HIDE);
	Invalidate();
}

void CDlgCalibration::OnBnClickedButtonExit()
{
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	showWindow(FALSE);
}

void CDlgCalibration::OnRButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	showWindow(FALSE);
	CDialogEx::OnRButtonDown(nFlags, point);
}


void CDlgCalibration::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	if (TID_WAIT == nIDEvent) {
		KillTimer(nIDEvent);
		m_nCalibrationStep = CALIBRATION_READ;
	}

	CDialogEx::OnTimer(nIDEvent);
}
